package com.jumi.microservice.stock.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.jumi.microservice.common.constant.*;
import com.jumi.microservice.common.exception.ExceptionEnum;
import com.jumi.microservice.common.utils.DateUtil;
import com.jumi.microservice.common.utils.StringProvider;
import com.jumi.microservice.material.dto.JmUnitResponse;
import com.jumi.microservice.material.dto.supplier.JmSupplierResponse;
import com.jumi.microservice.material.dto.warehouse.JmWarehouseResponse;
import com.jumi.microservice.material.mapper.JmGoodsMapper;
import com.jumi.microservice.material.service.JmWarehouseService;
import com.jumi.microservice.stock.domain.*;
import com.jumi.microservice.stock.mapper.StockCheckLogMapper;
import com.jumi.microservice.stock.mapper.StockConversionDetailMapper;
import com.jumi.microservice.stock.mapper.StockConversionMapper;
import com.jumi.microservice.stock.mapper.StockMaterialMapper;
import com.jumi.microservice.stock.service.CommonService;
import com.jumi.microservice.stock.service.StockConversionService;
import com.jumi.microservice.stock.service.StockService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Description
 * @ClassName StockConversionServiceImpl
 * @Author Abel Yang
 * @Date 2020/9/16/016 13:49
 **/
@Service
@Transactional(rollbackFor = Exception.class)
public class StockConversionServiceImpl implements StockConversionService {

    @Resource
    private StockService stockService;

    @Resource
    private JmWarehouseService jmWarehouseService;

    @Resource
    private CommonService commonService;

    @Resource
    private StockConversionMapper stockConversionMapper;

    @Resource
    private StockConversionDetailMapper stockConversionDetailMapper;

    @Resource
    private StockCheckLogMapper stockCheckLogMapper;

    @Resource
    private StockMaterialMapper stockMaterialMapper;
    @Resource
    private StockServiceImpl stockServiceImpl;

    @Override
    public Boolean addConversion(StockConversionApplyDTO apply, HttpServletRequest request) {

        //插入申请主表
        StockConversionDO conversion = new StockConversionDO();
        conversion.setGoodsId(apply.getGoodsId());
        conversion.setWarehouseId(apply.getWarehouseId());
        conversion.setConversionType(apply.getConversionType());
        conversion.setConversionTime(Objects.requireNonNull(DateUtil.str2TimeStamp(apply.getConversionTimeStr(),
                "yyyy-MM-dd HH:mm:ss")).intValue());
        conversion.setConversionRemark(apply.getConversionRemark());
        conversion.setTotalConversionNum(apply.getConversionApplyDetailList().stream().filter(k -> null != k.getConversionrNum()).mapToInt(StockConversionApplyDetailDTO::getConversionrNum).sum());
        conversion.setUpdateBy(request.getHeader(RequestConstant.ADMIN_NAME));
        conversion.setCreateBy(request.getHeader(RequestConstant.ADMIN_NAME));
        stockConversionMapper.insert(conversion);

        //插入申请详情
        apply.getConversionApplyDetailList().forEach(detail -> {
            //占用库存
            doOccupyStock(apply, request, detail);
            //详情
            StockConversionDetailDO detailDO = new StockConversionDetailDO();
            detailDO.setConversionId(conversion.getId());
            detailDO.setMaterialId(detail.getMaterialId());
            detailDO.setMaterialCode(detail.getMaterialCode());
            detailDO.setGoodsId(apply.getGoodsId());
            detailDO.setConversionNum(detail.getConversionrNum());
            detailDO.setUpdateBy(request.getHeader(RequestConstant.ADMIN_NAME));
            detailDO.setCreateBy(request.getHeader(RequestConstant.ADMIN_NAME));
            stockConversionDetailMapper.insert(detailDO);
        });
        return true;
    }

    private void doOccupyStock(StockConversionApplyDTO apply, HttpServletRequest request,
                               StockConversionApplyDetailDTO detail) {
        StockOccupy4WarehouseDTO occupy = new StockOccupy4WarehouseDTO();
        occupy.setMaterialId(detail.getMaterialId());
        occupy.setGoodsId(apply.getGoodsId());
        occupy.setWarehouseId(apply.getWarehouseId());
        occupy.setOccupyNum(detail.getConversionrNum());
        occupy.setUpdateBy(request.getHeader(RequestConstant.ADMIN_NAME));
        Integer types = apply.getConversionType() == 1 ? 38 : 39;
        occupy.setChangeType(types);
        occupy.setIsDamage(apply.getConversionType() == 1 ? false : true);
        stockService.occupyStock(occupy);
    }

    @Override
    public List<StockConversionVO> getConversionList(StockConversionSearchDTO stockConversionSearchDTO) {

        List<StockConversionVO> conversionList = stockConversionMapper.getConversionList(stockConversionSearchDTO);

        List<JmWarehouseResponse> warehouseResponseList = jmWarehouseService.getWarehouseList();
        Map<Long, String> warehouseMap =
                warehouseResponseList.stream().collect(Collectors.toMap(JmWarehouseResponse::getWarehouseId,
                        JmWarehouseResponse::getWarehouseName, (k1, k2) -> k2));

        Map<Integer, JmUnitResponse> unitMap =
                commonService.getUnitMapByIds(conversionList.stream().map(StockConversionVO::getUnitId).collect(Collectors.toList()));

        conversionList.forEach(conversion -> {
            JmUnitResponse unitResponse = null != unitMap.get(conversion.getUnitId()) ?
                    unitMap.get(conversion.getUnitId()) : new JmUnitResponse();
            conversion.setUnit(StringProvider.isNotEmpty(unitResponse.getSmunitName()) ?
                    unitResponse.getSmunitName() : "");
            conversion.setWarehouseName(StringProvider.isNotEmpty(warehouseMap.get(conversion.getWarehouseId())) ?
                    warehouseMap.get(conversion.getWarehouseId()) : "");
            conversion.setCheckTimeStr(conversion.getCheckTime() != 0 ?
                    DateUtil.time2String(conversion.getCheckTime().toString(), null) : "");
        });
        return conversionList;
    }

    @Override
    public StockConversionDetailVO getConversionDetail(Long conversionId) {

        //商品详情
        StockConversionDetailVO result = stockConversionMapper.getConversionGoodsById(conversionId);

        Map<Integer, JmUnitResponse> unitMap = commonService.getUnitMapByIds(Lists.newArrayList(result.getUnitId()));
        JmUnitResponse unitResponse = null != unitMap.get(result.getUnitId()) ? unitMap.get(result.getUnitId()) :
                new JmUnitResponse();

        Map<Long, JmSupplierResponse> supplierMap =
                commonService.getSupplierMapByIds(Lists.newArrayList(result.getSupplierId()));
        JmSupplierResponse supplierResponse = null != supplierMap.get(result.getSupplierId()) ?
                supplierMap.get(result.getSupplierId()) : new JmSupplierResponse();

        result.setSupplierCompanyName(StringProvider.isNotEmpty(supplierResponse.getSupplierCompanyName()) ?
                supplierResponse.getSupplierCompanyName() : "");

        //物料详情
        List<StockConversionDetailDTO> details = stockConversionDetailMapper.getDetailByConversionId(conversionId);
        details.forEach(detail -> {
            detail.setUnit(StringProvider.isNotEmpty(unitResponse.getSmunitName()) ? unitResponse.getSmunitName() : "");
        });
        result.setConversionDetailList(details);
        return result;
    }

    @Override
    public Boolean editConversion(StockConversionApplyDTO applyDTO, HttpServletRequest request) {
        StockConversionDO conversionDO = stockConversionMapper.selectById(applyDTO.getConversionId());
        if (null == conversionDO) {
            return false;
        }

        //更新商品记录
        conversionDO.setConversionTime(Objects.requireNonNull(DateUtil.str2TimeStamp(applyDTO.getConversionTimeStr(),
                "yyyy-MM-dd HH:mm:ss")).intValue());
        conversionDO.setConversionRemark(applyDTO.getConversionRemark());
        conversionDO.setTotalConversionNum(applyDTO.getConversionApplyDetailList().stream().map(StockConversionApplyDetailDTO::getConversionrNum).reduce(0, Integer::sum));
        conversionDO.setUpdateBy(request.getHeader(RequestConstant.ADMIN_NAME));
        conversionDO.setCheckStatus((byte) 0);
        stockConversionMapper.updateById(conversionDO);

        List<StockConversionDetailDO> detailDoList = stockConversionDetailMapper.selectList(
                Wrappers.<StockConversionDetailDO>lambdaQuery()
                        .select(StockConversionDetailDO::getId)
                        .eq(StockConversionDetailDO::getIsDel, IsDeletedEnum.NO)
                        .eq(StockConversionDetailDO::getConversionId, applyDTO.getConversionId())
        );
        if (detailDoList.size() == 0) {
            return false;
        }
        List<Long> detailIds = detailDoList.stream().map(StockConversionDetailDO::getId).collect(Collectors.toList());

        //更新详情（插入）
        applyDTO.getConversionApplyDetailList().forEach(detailDTO -> {
            //查询库存是否充足
            StockMaterialDO stockMaterial = stockMaterialMapper.selectOne(
                    Wrappers.<StockMaterialDO>lambdaQuery()
                            .eq(StockMaterialDO::getMaterialId, detailDTO.getMaterialId())
                            .eq(StockMaterialDO::getWarehouseId, applyDTO.getWarehouseId())
                            .eq(StockMaterialDO::getIsDel, IsDeletedEnum.NO)
            );
            ExceptionEnum.FAIL_STOCK_MATERIAL_NOT_EXISTS.doThrowIf(null == stockMaterial);
            assert stockMaterial != null;
            int availableStockNum = stockMaterial.getAvailableStockNum() - detailDTO.getConversionrNum();
            ExceptionEnum.FAIL_STOCK_AVAILABLE_STOCK_NOT_ENOUGH.doThrowIf(availableStockNum < 0);

            StockConversionDetailDO detailDO = new StockConversionDetailDO();
            detailDO.setConversionNum(detailDTO.getConversionrNum());
            detailDO.setUpdateBy(request.getHeader(RequestConstant.ADMIN_NAME));

            if (detailIds.contains(detailDTO.getConversionDetailId())) {
                detailDO.setId(detailDTO.getConversionDetailId());
                stockConversionDetailMapper.updateById(detailDO);
            } else {
                detailDO.setConversionId(applyDTO.getConversionId());
                detailDO.setMaterialId(detailDTO.getMaterialId());
                detailDO.setMaterialCode(detailDTO.getMaterialCode());
                detailDO.setGoodsId(applyDTO.getGoodsId());
                detailDO.setCreateBy(request.getHeader(RequestConstant.ADMIN_NAME));
                stockConversionDetailMapper.insert(detailDO);
            }
        });
        return true;
    }

    @Override
    public Boolean auditConversion(StockConversionAuditDTO auditDTO, HttpServletRequest request) {

        Integer currentTime = Objects.requireNonNull(DateUtil.str2TimeStamp(DateUtil.getTime(),
                "yyyy-MM-dd HH:mm:ss")).intValue();

        auditDTO.getConversionIds().forEach(conversionId -> {
            //查询申请记录
            StockConversionDO conversion = stockConversionMapper.selectById(conversionId);

            //校验是否已经通过
            ExceptionEnum.FAIL_APPLY_IS_ALREADY_CHECKED.doThrowIf(StockCheckStatusEnum.STATUS_CHECKED.getStatus().equals(conversion.getCheckStatus()));

            //通过
           /* if (StockCheckStatusEnum.STATUS_CHECKED.getStatus().equals(auditDTO.getCheckStatus())) {
                //转移物料库存
                conversionStock(conversion,auditDTO.getCheckStatus());
            }*/
            conversionStock(conversion,auditDTO);

            conversion.setCheckStatus(auditDTO.getCheckStatus());
            conversion.setCheckTime(currentTime);
            conversion.setCheckName(request.getHeader(RequestConstant.ADMIN_NAME));
            conversion.setUpdateBy(request.getHeader(RequestConstant.ADMIN_NAME));
            conversion.setCheckRemark(auditDTO.getCheckRemark());
            stockConversionMapper.updateById(conversion);

            //添加审核日志
            StockCheckLogDO checkLog = new StockCheckLogDO();
            checkLog.setApplyId(conversionId);
            checkLog.setCheckType(StockTransferTypeEnum.TYPE_IN_WAREHOUSE.getType());
            checkLog.setTotalNum(conversion.getTotalConversionNum());
            checkLog.setCheckStatus(auditDTO.getCheckStatus());
            checkLog.setCheckName(request.getHeader(RequestConstant.ADMIN_NAME));
            checkLog.setCheckRemark(auditDTO.getCheckRemark());
            checkLog.setCreateBy(request.getHeader(RequestConstant.ADMIN_NAME));
            checkLog.setUpdateBy(request.getHeader(RequestConstant.ADMIN_NAME));
            stockCheckLogMapper.insert(checkLog);
        });
        return true;
    }

    private void conversionStock(StockConversionDO conversion,StockConversionAuditDTO auditDTO) {

        //转移详情列表
        List<StockConversionDetailDTO> conversionDetailList =
                stockConversionDetailMapper.getDetailByConversionId(conversion.getId());
        ExceptionEnum.FAIL_CONVERSION_DETAIL_IS_EMPTY.doThrowIf(conversionDetailList.size() == 0);

        conversionDetailList.forEach(conversionDetail -> {
            StockMaterialDO stockMaterial = stockMaterialMapper.selectOne(
                    Wrappers.<StockMaterialDO>lambdaQuery()
                            .eq(StockMaterialDO::getWarehouseId, conversion.getWarehouseId())
                            .eq(StockMaterialDO::getMaterialId, conversionDetail.getMaterialId())
            );
            ExceptionEnum.FAIL_STOCK_MATERIAL_NOT_EXISTS.doThrowIf(null == stockMaterial);

            int conversionNum = conversionDetail.getConversionNum();

            LambdaUpdateWrapper<StockMaterialDO> updateWrapper = new LambdaUpdateWrapper<>();

            assert stockMaterial != null;
            updateWrapper.eq(StockMaterialDO::getId, stockMaterial.getId());
            int originStockNum = 0;
            String checkNames = !conversion.getCheckName().equals("") ? conversion.getCheckName() :
                    conversion.getCreateBy();
            //通过
            if(StockCheckStatusEnum.STATUS_CHECKED.getStatus().equals(auditDTO.getCheckStatus())){
                //正规转破损
                if (StockConversionTypeEnum.TYPE_TO_DAMAGE.getType().equals(conversion.getConversionType())) {
                    originStockNum = stockMaterial.getAvailableStockNum();
                    updateWrapper.setSql("damage_stock_num = damage_stock_num +" + conversionNum)
                            .setSql("occupy_num = occupy_num -" + conversionNum);
                    //占用日志
                    String backups = "调仓调入破损仓:"+conversion.getId();
                    stockServiceImpl.logAdd(conversionDetail.getMaterialId(),conversion.getGoodsId(),conversion.getWarehouseId(),
                            StockChangeTypeEnum.TYPE_TRANSFER_TO_DAMAGE.getChangeType(), conversionNum,
                            (byte)0,"",checkNames,(byte)1,stockMaterial.getOccupyNum(),
                            backups);
                    //破损日志明细
                    String backupDamage ="调仓调入破损仓:"+conversion.getId();
                    stockServiceImpl.logAdd(conversionDetail.getMaterialId(),conversion.getGoodsId(),conversion.getWarehouseId(),
                            StockChangeTypeEnum.TYPE_TRANSFER_TO_DAMAGE.getChangeType(), conversionNum,
                            (byte)1,"",checkNames,(byte)2,stockMaterial.getDamageStockNum(),
                            backupDamage);
                } else {//破损转正规
                    originStockNum = stockMaterial.getDamageStockNum();
                    updateWrapper.setSql("available_stock_num = available_stock_num +" + conversionNum)
                            .setSql("damage_stock_num = damage_stock_num -" + conversionNum);
                    //破损日志明细
                    String backupDamage = "调仓调入正规仓:"+conversion.getId();
                    stockServiceImpl.logAdd(conversionDetail.getMaterialId(),conversion.getGoodsId(),conversion.getWarehouseId(),
                            StockChangeTypeEnum.TYPE_TRANSFER_TO_DAMAGE.getChangeType(), conversionNum,
                            (byte)0,"",checkNames,(byte)2,stockMaterial.getDamageStockNum(),
                            backupDamage);
                }
                int left = originStockNum - conversionNum;
                ExceptionEnum.FAIL_STOCK_AVAILABLE_STOCK_NOT_ENOUGH.doThrowIf(left < 0);
                //判断是否触发预警
                updateWrapper.set(StockMaterialDO::getAlarmed, stockMaterial.getAlarmNum() > left);
            }else{
                //正规转破损
                if (StockConversionTypeEnum.TYPE_TO_DAMAGE.getType().equals(conversion.getConversionType())) {
                    originStockNum = stockMaterial.getAvailableStockNum();
                    updateWrapper.setSql("available_stock_num = available_stock_num +" + conversionNum)
                            .setSql("occupy_num = occupy_num -" + conversionNum);
                    //占用日志
                    String backups = "取消调仓调入破损仓:"+conversion.getId();
                    stockServiceImpl.logAdd(conversionDetail.getMaterialId(),conversion.getGoodsId(),conversion.getWarehouseId(),
                            StockChangeTypeEnum.TYPE_CANCEL_TO_DAMAGE.getChangeType(), conversionNum,
                            (byte)0,"",checkNames,(byte)1,stockMaterial.getOccupyNum(),
                            backups);

                } else {//破损转正规
                    originStockNum = stockMaterial.getDamageStockNum();
                    updateWrapper.setSql("damage_stock_num = damage_stock_num +" + conversionNum);
                    //破损日志明细
                    String backupDamage = "取消调仓调入正规仓:"+conversion.getId();
                    stockServiceImpl.logAdd(conversionDetail.getMaterialId(),conversion.getGoodsId(),conversion.getWarehouseId(),
                            StockChangeTypeEnum.TYPE_CANCEL_TO_NORMAL.getChangeType(), conversionNum,
                            (byte)1,"",conversion.getCheckName(),(byte)2,stockMaterial.getDamageStockNum(),
                            backupDamage);
                }
            }
            stockMaterialMapper.update(new StockMaterialDO(), updateWrapper);
        });
    }
}
